home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
17 Bit Software 3: The Continuation
/
17-Bit_The_Continuation_Disc.iso
/
amigan
/
amigan 18
/
emit_v2.0
/
emit.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-01-27
|
18KB
|
603 lines
/* --------------------------------------------------------------------
* NAME
* Emit V2.0 by Justin V. McCormick 1/29/88
*
* Placed by the Author in the Public Domain -- freely reproducable
* in any form so long as this notice remains intact.
*
* Some Rights Reserved (i.e., I will not be held accountable for the
* use or misuse of this code).
*
* SYNOPSIS
* emit -[s][r] [filename]
* -s Send filename over serial port
* -r Receive data from serial port and store in "filename"
*
* DESCRIPTION
* Emit is a high-speed serial transfer utility useful for moving
* files from one Amiga to another via a null modem cable. Emit
* operates at 280,000 baud, and its actual data transfer rate has
* been benchmarked at over 24,000 bytes per second.
*
* You should type "emit -s foo" on the sending Amiga to transmit
* a file named "foo". The sending Amiga will attempt to access
* the file "foo", and await a handshake signal from the receiving
* Amiga. On the receiving Amiga, you should then type "emit -r foo"
* to download the file. As soon as the handshake has been
* established, transfer of the file begins. You can use Control-C
* to break out of the program during execution.
*
* The idea behind Emit was to create a useful tool for downloading
* code to a target machine for testing. For instance, in my
* development enviroment, I compile my code on a Amiga 1000, then
* zap the code over the serial port to an Amiga 500 for testing.
* If the code bombs and the Guru visits, no sweat; my environment
* on the 1000 is safe (sorta, I still use vdk: and make lots of
* intermediate backups!). The benefits of a dual-machine development
* enviroment are terrific: I insure that the code works on both the
* 1000 and the 500, I have a fairly safe development environment,
* and I can "Wack" the code through the port using the 1000 as a
* debugging terminal when the going gets tough. I would estimate
* that moving to a dual-machine setup has doubled the efficiency
* of my development time. I use to do a lot of disk swapping
* before writing Emit; I propose that this program will greatly
* increase the lifespans of my disk-eject mechanisms.
*
* Emit was written very quick-and-dirty. It shows. I wrote it as
* an experiment to see how fast Exec can handle the serial port,
* and because there were no programs available that did what I
* needed. For instance, I tried using several terminal programs,
* both commercial and public domain. Only Online! 2.0 would perform
* reliably at 19200 baud, and I felt ridiculous running a 176K
* program to download a measly couple of bytes. (BTW, Diga! NEVER
* managed to do better than 4800 baud on ANY setting, according
* to my stop-watch). Also, the RKM 1 mentions that baud rates
* up to 292,000 may be specified (though your mileage may vary),
* so I was itching to try it.
*
* Note that this code is *NOT* to be taken as a proper example of
* how to control the serial port -- it merely works for me. I do
* several deplorable things for the sake of speed like busy-waiting,
* disabling interrupts, poking and peeking at hardware registers,
* and making unwarrented assumptions about the hardware that
* SHOULD NEVER be done if you want your code to work with
* future versions of the Amiga hardware.
*
* FILES
* Emit * The executable code
* emit.c * Source to main routine, control module
* ser.asm * Source to assembly serial read/write routines
* aprintf.asm * Hand-rolled printf() replacement
* macros.i * Assembly includes
* i.pre.h * A kitchen-sink include file
* makefile * Makes it
*
* Written using Manx Aztec C 3.4b (thanks Jim!)
* You might want to precompile i.pre.h to speed compiling.
*
* DIAGNOSTICS
* Emit has fairly good error detection and recovery facilities.
* Typical problems that could crop up are bad filenames, file not
* found, inability to open a file, insufficient RAM, serial port
* problems, and serial flow control difficulties. If a problem is
* detected, Emit will exit and describe the problem, possibly with
* a standard AmigaDOS error number or Exec serial problem number.
*
* BUGS
* Undoubtably. Most of the known problems exist on the receive side
* of the program. The Emit sender sends the filesize to the Emit
* receiver; the receiver gets only one chance to grab this
* critical information. No error-detection/correction/checksumming
* is done on transfered data. When receiving files, Emit creates
* an input buffer the same size as the file to be downloaded; if
* there is insufficient RAM for the buffer the transfer will fail.
* I put an arbitrary 500K limit on the filesize that can be
* downloaded -- feel free to change this if it annoys you.
* I am being rather rude, crude, and high-handed when I access
* the serial port, and while the program has proven reliable for
* me, I can't promise there isn't some problem in there that might
* bite one day.
*
* Any comments, improvements, or discussion of techniques are
* welcome. You can reach me at:
*
* Justin V. McCormick
* 100 S. Meyers, Apt #1723
* Lafayette, LA 70508
*
* Voice: 1-318-981-1314
* PLINK: progress**
* BIX: dbrowning
*
* -------------------------------------------------------------------- */
#include "i.pre.h"
#define BAUD 280000L /* Baud Rate */
#define BUFSIZE 8192L /* Size of output buffer */
/* Externs */
extern int Enable_Abort; /* We want to process ^C ourself */
/* Global functions */
char *extractfilename();
/* Global variables */
int updownflag = 0; /* 0 for upload, 1 for download */
char filename[300]; /* Filename string */
char progname[34]; /* What the program is called */
long filesize; /* Byte length of file to upload */
/* File related variables */
struct FileHandle *emitfile = 0L; /* Pointer to stream opened */
struct FileInfoBlock *eFib = 0L; /* General purpose Fib pointer */
struct FileLock *eLock = 0L; /* FileLock pointer */
long buffersize = 0L; /* Size of input buffer */
UBYTE *outbuf = 0L; /* Pointer to input buffer */
/* STUFF FOR SERIAL IO */
#define SRPORTNAME "SerReadPort"
#define SWPORTNAME "SerWritePort"
ULONG S_Sig; /* Signal masks for ports */
/* globals functions */
int OpenSerial ();
int CloseSerial ();
/* global structures */
struct IOExtSer *RReq = 0L; /* Pointer to Read Request block */
struct IOExtSer *WReq = 0L; /* Pointer to Write Request block */
struct MsgPort *SerRPort = 0L; /* Serial Read Port */
struct MsgPort *SerWPort = 0L; /* Serial Write Port */
/* other globals */
long error = 0L; /* global error return status */
ULONG openflags = 0L; /* devices we opened */
UBYTE aserinbuf[2]; /* asynchronous serial input buffer */
UBYTE *inbuf = 0L; /* private serial input buffer */
struct FileHandle *cliout; /* Standard output filehandle */
/* openflags defines */
#define SRDEV_OPENED 1 /* set if opened read device */
#define SWDEV_OPENED 2 /* set if opened write device */
/* -------------------------------------------------------------------- */
/* MAIN CODE */
/* -------------------------------------------------------------------- */
main (argc, argv)
int argc;
char **argv;
{
register long status;
register long x;
/* Do our own ^C processing, thank you */
Enable_Abort = 0;
/* Where's output go? */
cliout = Output();
if (cliout == 0L)
CleanUp("");
/* Allocate a FileInfoBlock */
if ( (eFib = (struct FileInfoBlock *)AllocMem((long)sizeof(struct FileInfoBlock), MEMF_PUBLIC | MEMF_CLEAR)) == 0L)
CleanUp("No RAM");
/* Parse command line arguments */
strcpy(progname, extractfilename(argv[0]));
if (argc < 3)
ShowUsage(); /* Not enough args */
if (argv[1][0] != '-')
ShowUsage(); /* Option didn't start with a '-' */
/* Upload or Download? */
switch (argv[1][1])
{
case 's':
case 'S':
updownflag = 0;
break;
case 'r':
case 'R':
updownflag = 1;
break;
default:
ShowUsage();
break;
}
/* Copy off argv[2], assume it is filename */
strcpy(filename, argv[2]);
/* Open file */
if (updownflag == 1) /* Create and open file for download */
emitfile = Open(filename, MODE_NEWFILE);
else
{
/* Allocate input buffer */
if ( (inbuf = (UBYTE*)AllocMem(BUFSIZE, MEMF_PUBLIC|MEMF_CLEAR)) == 0L)
CleanUp("No ram for buffer!");
/* Grab a Lock the file */
if ( (eLock = (struct FileLock *)Lock(filename, ACCESS_READ)) == 0L)
{
CleanUp("can't lock '%s', IoErr %ld", filename, IoErr());
}
/* Examine the root block */
if ( !Examine(eLock, eFib) )
{
CleanUp("can't examine '%s', IoErr %ld", filename, IoErr());
}
/* Is it a file? */
if (eFib->fib_DirEntryType >= 0L)
{
CleanUp("'%s' is a directory, not a file", filename);
}
/* Grab the length */
filesize = eFib->fib_Size;
/* Open the file for upload */
emitfile = Open(filename, MODE_OLDFILE);
}
if (emitfile == 0L)
{
CleanUp("can't open '%s', IoErr %ld", filename, IoErr() );
}
/* Do heavy Amiga Kernal Magic to set up serial I/O */
OpenSerial();
/* Do the first async read to initialize the read port */
GetASer();
/* Enter the control loop */
if (updownflag == 0) /* Upload */
do_upload();
else /* Download */
do_download();
/* Must be done */
CleanUp("done");
}
/* -------------------------------------------------------------------- */
/* Describe correct usage, cleanup */
/* -------------------------------------------------------------------- */
ShowUsage()
{
aprintf("\x9b1;33;40mEmit V2.0\x9b0m -- 280,000 Baud Serial File Transfer Utility\n\
\tFreeware by Justin V. McCormick 1/29/88\n\n\
\x9b1;33;40mUsage:\x9b0m %s -[s][r] [filename]\n\
\t-s == send\n\
\t-r == receive\n", progname);
CleanUp("");
}
/*---------------------------- CleanUp -----------------------------------*/
/* Close up everything and exit.
*/
CleanUp (msg, arg1, arg2, arg3)
char *msg, *arg1, *arg2, *arg3;
{
if (inbuf)
FreeMem(inbuf, BUFSIZE);
if (eLock)
UnLock(eLock);
if (eFib)
FreeMem(eFib, (long)sizeof(struct FileInfoBlock));
if (outbuf)
FreeMem(outbuf, buffersize);
if (emitfile)
Close(emitfile);
CloseSerial ();
if (*msg)
{
aprintf ("\x9b1;33;40m%s:\x9b0m ", progname);
aprintf(msg, arg1, arg2, arg3);
aprintf(".\n");
}
exit (0);
}
/* -------------------------------------------------------------------- */
/* Given a full pathname, return just the filename */
/* -------------------------------------------------------------------- */
char *extractfilename(name)
register char *name;
{
register char *tmp;
tmp = rindex(name, '/');
if (!tmp)
{
tmp = rindex(name, ':');
if (!tmp)
tmp = name;
else
tmp++;
}
else
tmp++;
if (strlen(tmp) > 30)
tmp[30] = '\0';
return(tmp);
}
/* -------------------------------------------------------------------- */
/* Override Manx declaration, we will process ^C ourself */
/* -------------------------------------------------------------------- */
Chk_Abort ()
{
return (0);
}
/* -------------------------------------------------------------------- */
/* See if user hit ^C */
/* -------------------------------------------------------------------- */
CheckCntrlC()
{
if (SetSignal (0L, 0L) & SIGBREAKF_CTRL_C)
{
aprintf("^C");
return (1);
}
else
return(0);
}
/* -------------------------------------------------------------------- */
/* Upload a file asap */
/* -------------------------------------------------------------------- */
do_upload()
{
register long length;
long blksize = BUFSIZE;
/* Let the user in on what's happening */
aprintf("Filesize to upload: %ld bytes\n", filesize);
aprintf("Waiting for ENQ...");
fflush(stdout);
/* Might be some trash or ENQ's already, flush it out */
RReq->IOSer.io_Command = CMD_FLUSH;
if (DoIO (RReq) != 0L)
CleanUp ("Can't flush the serial device!");
/* Wait for ENQ character from dest */
while (1)
{
if (GotENQ()) /* Check for ENQ */
break;
if (CheckCntrlC())
CleanUp("upload aborted");
Delay(5L);
}
SerialWrite("\x06", 1L); /* Send a ACK */
aprintf("ENQ\nSending file...\n");
Delay(15L);
SerialWrite(&filesize, 4L);
Delay(25L);
aprintf("%ld\n", filesize);
/* Dump the file */
while (filesize)
{
if (filesize < blksize)
blksize = filesize;
length = Read(emitfile, inbuf, blksize);
if (length != blksize)
{
CleanUp("can't read file, IoErr %ld", IoErr());
}
SerialWrite(inbuf, blksize);
filesize = filesize - blksize;
aprintf("\x9b\x46\x0d\x9b\x4b%ld\n", filesize);
if (CheckCntrlC())
CleanUp("upload aborted");
}
}
/* -------------------------------------------------------------------- */
/* Download a file pronto */
/* -------------------------------------------------------------------- */
do_download()
{
register long length;
register UBYTE *bufp;
register long blksize;
aprintf("Waiting for ACK...");
fflush(stdout);
/* Enquire of host at regular intervals, wait for ACK or abort */
while (1)
{
if (GotACK()) /* Got an ACK */
break;
if (CheckCntrlC()) /* Got an abort */
CleanUp("Aborted");
SerialWrite ("\x05", 1L); /* Otherwise, send an ENQ */
Delay (10L); /* wait 1/5th sec. */
}
aprintf("ACK\n");
/* Get filesize to download */
SerialRead(&filesize, 4L);
if (filesize <= 0 || filesize > 500000L)
{
CleanUp("Filesize %ld out of range", filesize);
}
aprintf("Filesize to download: %ld bytes\n Reading data...\n", filesize);
fflush(stdout);
/* Allocate input buffer */
buffersize = filesize;
if ( (outbuf = AllocMem(buffersize, MEMF_PUBLIC | MEMF_CLEAR)) == 0L)
CleanUp("can't allocate input buffer");
/* Pull in the rest of the characters */
SerialRead(outbuf, filesize);
/* Write the buffer to file */
aprintf(" Writing file...\n");
Write(emitfile, outbuf, buffersize);
}
/* -------------------------- OpenSerial ------------------------------ */
/* Open the serial device twice, once for read and once for write. */
/* -------------------------------------------------------------------- */
int OpenSerial ()
{
/* Open the ports */
if ((SerRPort = CreatePort (SRPORTNAME, 0L)) == 0L ||
(SerWPort = CreatePort (SWPORTNAME, 0L)) == 0L)
{
CleanUp ("can't open SerRPort or SerWPort");
}
/* Create the request blocks */
if ((RReq = (struct IOExtSer *) CreateExtIO (SerRPort, (long) sizeof (*RReq))) == 0L ||
(WReq = (struct IOExtSer *) CreateExtIO (SerWPort, (long) sizeof (*WReq))) == 0L)
{
CleanUp ("can't open RReq or WReq");
}
/* Set io_SerFlags to share them ports */
RReq->io_SerFlags = WReq->io_SerFlags = SERF_SHARED;
/* Open the device once for each request block */
if ((error = OpenDevice (SERIALNAME, 0L, RReq, 0L)) != 0L)
CleanUp ("can't open read device!");
else
openflags |= SRDEV_OPENED; /* Successfully opened read device */
if ((error = OpenDevice (SERIALNAME, 0L, WReq, 0L)) != 0L)
CleanUp ("can't open write device!");
else
openflags |= SWDEV_OPENED; /* Successfully opened write device */
/* Set our wakeup signal mask for the serial read port */
S_Sig = 1L << SerRPort->mp_SigBit;
/* Set serial parameters */
RReq->io_Baud = BAUD;
WReq->io_Baud = BAUD;
RReq->io_RBufLen = 8000L;
WReq->io_RBufLen = 8000L;
RReq->io_ReadLen = (UBYTE)8;
WReq->io_ReadLen = (UBYTE)8;
RReq->io_WriteLen = (UBYTE)8;
WReq->io_WriteLen = (UBYTE)8;
RReq->io_StopBits = (UBYTE)1;
WReq->io_StopBits = (UBYTE)1;
RReq->io_SerFlags = SERF_SHARED | SERF_RAD_BOOGIE;
WReq->io_SerFlags = SERF_SHARED | SERF_RAD_BOOGIE;
RReq->IOSer.io_Command = SDCMD_SETPARAMS;
WReq->IOSer.io_Command = SDCMD_SETPARAMS;
if (DoIO(RReq) || DoIO(WReq))
CleanUp("can't set serial parameters");
}
/*----------------------------- CloseSerial -------------------------------*/
int CloseSerial ()
{
struct IOExtSer *tReq;
if (RReq)
{
if (openflags & SRDEV_OPENED)
{
while ((tReq = (struct IOExtSer *) CheckIO (RReq)) == 0L)
{
AbortIO (RReq);
WaitIO (RReq);
GetMsg (SerRPort);
}
CloseDevice (RReq);
}
DeleteExtIO (RReq, (long)sizeof(*RReq) );
}
if (WReq)
{
if (openflags & SWDEV_OPENED)
CloseDevice (WReq);
DeleteExtIO (WReq, (long)sizeof(*WReq) );
}
if (SerRPort)
DeletePort (SerRPort);
if (SerWPort)
DeletePort (SerWPort);
}
int GotACK()
{
struct IOExtSer *tReq;
register char tempchar;
if ((tReq = (struct IOExtSer *) CheckIO (RReq)) == 0L)
return (0L);
WaitIO (RReq);
tempchar = aserinbuf[0] & '\x7f';
GetASer();
if (tempchar == '\x06')
return(1);
else
return(0);
}
int GotENQ()
{
struct IOExtSer *tReq;
register char tempchar;
if ((tReq = (struct IOExtSer *) CheckIO (RReq)) == 0L)
return (0L);
WaitIO (RReq);
tempchar = aserinbuf[0] & '\x7f';
GetASer();
if (tempchar == '\x05')
return(1);
else
return(0);
}
/* --------------------------------- GetASer -------------------------- */
/* Send a async read request to the serial port for 1 character */
/* -------------------------------------------------------------------- */
GetASer ()
{
RReq->IOSer.io_Command = CMD_READ;
RReq->IOSer.io_Length = 1L;
RReq->IOSer.io_Data = (APTR) &aserinbuf[0];
SendIO (RReq);
}